<?php

namespace Xnrcms\BaseTools;

use Exception;

class LicenseGenerator
{
    const DATA_LENGTH = 30;
    const CHECKSUM_LENGTH = 2;
    const TOTAL_LENGTH = 32;
    const CHARS = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ';

    /**
     * 生成高强度许可证
     * @return string
     * @throws Exception
     */
    public static function generate(): string
    {
        do {
            $data = self::generateSecureRandom(self::DATA_LENGTH);
            $checksum = self::calculateChecksum($data);
            $license = self::formatLicense($data.$checksum);
        } while (!self::validate($license)); // 双重验证保障

        return $license;
    }

    // 验证许可证有效性
    public static function validate($license): bool
    {
        // 标准化处理
        $clean = strtoupper(preg_replace('/[^A-Z\d]/', '', $license));

        // 基础格式验证
        if (strlen($clean) !== self::TOTAL_LENGTH) return false;
        if (!preg_match('/^[A-Z\d]{'.self::TOTAL_LENGTH.'}$/', $clean)) return false;

        // 分离数据与校验码
        $data = substr($clean, 0, self::DATA_LENGTH);
        $checksum = substr($clean, self::DATA_LENGTH, self::CHECKSUM_LENGTH);

        return self::calculateChecksum($data) === $checksum;
    }

    /**
     * 获取随机数据
     * @param int $length
     * @return string
     * @throws Exception
     */
    private static function generateSecureRandom(int $length = 0): string
    {
        $result = '';
        for ($i = 0; $i < $length; $i++) {
            $result .= self::CHARS[random_int(0, 35)]; // 使用加密安全随机数
        }
        return $result;
    }

    private static function calculateChecksum($data): string
    {
        // 改进校验算法：CRC16 + 权重校验
        $crc = self::crc16IBM($data);
        $weightSum = self::weightedSum($data);
        $combined = ($crc + $weightSum) % 1296; // 36^2

        return
            self::CHARS[(int)($combined / 36)] .
            self::CHARS[$combined % 36];
    }

    private static function crc16IBM($data): int
    {
        $crc = 0xFFFF;
        for ($i = 0; $i < strlen($data); $i++) {
            $crc ^= ord($data[$i]) << 8;
            for ($j = 0; $j < 8; $j++) {
                $crc = $crc & 0x8000 ? ($crc << 1) ^ 0x8005 : $crc << 1;
                $crc &= 0xFFFF;
            }
        }
        return $crc;
    }

    private static function weightedSum($data): float|int
    {
        $sum = 0;
        $weights = [2,3,5,7,11,13,17,19,23,29,31,37,41,43,47,53,59,61,67,71,73,79,83,89,97,101,103,107,109,113];
        for ($i = 0; $i < strlen($data); $i++) {
            $value = strpos(self::CHARS, $data[$i]);
            $sum += $value * $weights[$i];
        }
        return $sum;
    }

    private static function formatLicense($license): string
    {
        $parts = [];
        // 分割格式：5-5-5-5-5-5-2
        foreach ([5,5,5,5,5,5,2] as $chunk) {
            $parts[] = substr($license, 0, $chunk);
            $license = substr($license, $chunk);
        }
        return implode('-', $parts);
    }
}